package utils

import (
	"fmt"
	"strings"
)

// DockerfileCmd https://docs.docker.com/reference/dockerfile/
type DockerfileCmd string

const (
	// DockerfileCmdADD 添加本地或远程文件和目录
	DockerfileCmdADD DockerfileCmd = "ADD"

	// DockerfileCmdARG 使用构建时变量
	DockerfileCmdARG DockerfileCmd = "ARG"

	// DockerfileCmdCMD 指定默认命令
	DockerfileCmdCMD DockerfileCmd = "CMD"

	// DockerfileCmdCOPY 复制文件和目录
	DockerfileCmdCOPY DockerfileCmd = "COPY"

	// DockerfileCmdENTRYPOINT 指定默认可执行文件。
	DockerfileCmdENTRYPOINT DockerfileCmd = "ENTRYPOINT"

	// DockerfileCmdENV 设置环境变量。
	DockerfileCmdENV DockerfileCmd = "ENV"

	// DockerfileCmdEXPOSE 描述您的应用程序正在监听哪些端口。
	DockerfileCmdEXPOSE DockerfileCmd = "EXPOSE"

	// DockerfileCmdFROM 从基础图像创建一个新的构建阶段
	DockerfileCmdFROM DockerfileCmd = "FROM"

	// DockerfileCmdHEALTHCHECK 在启动时检查容器的健康状况。
	DockerfileCmdHEALTHCHECK DockerfileCmd = "HEALTHCHECK"

	// DockerfileCmdLABEL 向图像添加元数据。
	DockerfileCmdLABEL DockerfileCmd = "LABEL"

	// DockerfileCmdMAINTAINER 指定图像的作者。
	DockerfileCmdMAINTAINER DockerfileCmd = "MAINTAINER"

	// DockerfileCmdONBUILD 指定在构建中使用图像时的说明
	DockerfileCmdONBUILD DockerfileCmd = "ONBUILD"

	// DockerfileCmdRUN 执行构建命令
	DockerfileCmdRUN DockerfileCmd = "RUN"

	// DockerfileCmdSHELL 设置图像的默认外壳。
	DockerfileCmdSHELL DockerfileCmd = "SHELL"

	// DockerfileCmdSTOPSIGNAL 指定退出容器的系统调用信号。
	DockerfileCmdSTOPSIGNAL DockerfileCmd = "STOPSIGNAL"

	// DockerfileCmdUSER 设置用户和组 ID。
	DockerfileCmdUSER DockerfileCmd = "USER"

	// DockerfileCmdVOLUME 创建卷挂载。
	DockerfileCmdVOLUME DockerfileCmd = "VOLUME"

	// DockerfileCmdWORKDIR 改变工作目录。
	DockerfileCmdWORKDIR DockerfileCmd = "WORKDIR"
)

func (dc DockerfileCmd) String() string {
	return string(dc)
}

var AllDockerfileCmds = [...]DockerfileCmd{
	DockerfileCmdFROM,
	DockerfileCmdWORKDIR,
	DockerfileCmdRUN,
	DockerfileCmdCOPY,
	DockerfileCmdCMD,
	DockerfileCmdADD,
	DockerfileCmdARG,
	DockerfileCmdENV,
	DockerfileCmdEXPOSE,
	DockerfileCmdENTRYPOINT,
	DockerfileCmdLABEL,
	DockerfileCmdMAINTAINER,
	DockerfileCmdUSER,
}

var AllDockerfileCmdStrings = [...]string{
	DockerfileCmdFROM.String(),
	DockerfileCmdWORKDIR.String(),
	DockerfileCmdRUN.String(),
	DockerfileCmdCOPY.String(),
	DockerfileCmdCMD.String(),
	DockerfileCmdADD.String(),
	DockerfileCmdARG.String(),
	DockerfileCmdENV.String(),
	DockerfileCmdEXPOSE.String(),
	DockerfileCmdENTRYPOINT.String(),
	DockerfileCmdLABEL.String(),
	DockerfileCmdMAINTAINER.String(),
	DockerfileCmdUSER.String(),
}
var DockerfileCmdMap map[string]DockerfileCmd = func() map[string]DockerfileCmd {
	m := map[string]DockerfileCmd{}
	for _, cmd := range AllDockerfileCmds {
		m[cmd.String()] = cmd
	}
	return m
}()

func GetDockerfileCmd(s string) DockerfileCmd {
	return DockerfileCmdMap[s]
}

// DockerfileStage 构建阶段
type DockerfileStage struct {
	Name               string              `json:"name"`
	DockerfileCmdLines []DockerfileCmdLine `json:"dockerfileCmdLines"`
}

type DockerfileCmdLine struct {
	DockerfileCmd DockerfileCmd `json:"dockerfileCmd"`
	Params        []string      `json:"params"`
}

type DockerfileCmdConvert interface {
	CmdConvertFunc(...string) string
}

func (dc DockerfileCmd) CmdConvertFunc(ss ...string) string {
	str := fmt.Sprintf("%s %s", dc.String(), strings.Join(ss, " "))
	switch dc {
	case DockerfileCmdENTRYPOINT, DockerfileCmdCMD:
		if len(ss) > 1 {
			for i, s2 := range ss {
				ss[i] = fmt.Sprintf("[\"%s\"]", s2)
			}
			str = fmt.Sprintf("%s %s", dc.String(), strings.Join(ss, " "))
		}
	}
	return str
}

// GenerateDockerfile 生成Dockerfile文件
func GenerateDockerfile(stages ...DockerfileStage) (string, error) {
	var stringBuilder strings.Builder
	for _, stage := range stages {
		stageName := stage.Name
		lines := stage.DockerfileCmdLines
		line := lines[0]
		if line.DockerfileCmd != DockerfileCmdFROM {
			return "", GENERATE_DOCKERFILE_ERR_NOT_FROM
		}
		fromStr := line.DockerfileCmd.CmdConvertFunc(line.Params...)
		if len(stageName) != 0 {
			fromStr = fmt.Sprintf("%s as %s", fromStr, stageName)
		}
		stringBuilder.WriteString(fromStr)
		stringBuilder.WriteString("\n")

		for i, cmdLine := range lines {
			if i == 0 {
				continue
			}
			stringBuilder.WriteString(cmdLine.DockerfileCmd.CmdConvertFunc(cmdLine.Params...))
			stringBuilder.WriteString("\n")
		}
		stringBuilder.WriteString("\n")
	}
	return stringBuilder.String(), nil
}

// GENERATE_DOCKERFILE_ERR 自定义错误类型
type GENERATE_DOCKERFILE_ERR string

const (
	GENERATE_DOCKERFILE_ERR_NOT_FROM GENERATE_DOCKERFILE_ERR = "the first DockerfileCmd is not FROM cmd!"
)

func (r GENERATE_DOCKERFILE_ERR) Error() string {
	return string(r)
}

func init() {
	for _, cmd := range AllDockerfileCmds {
		DockerfileCmdMap[cmd.String()] = cmd
	}
}
